package com.fingard.dsp.tcpserver;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.fingard.FGBiz;
import com.fingard.dsp.core.DSPSecurityData;
import com.fingard.text.StringHelper;

public class ServerSocketByThread implements Runnable {
    
	//private String _ListenerIP = "";
    private int listenPort = 0;
    
	/**
     * 构造函数
     */
    public ServerSocketByThread(int p_listenPort)
    {
    	//String p_ListenerIP,
    	//_ListenerIP = p_ListenerIP;
    	listenPort = p_listenPort;
    }
    
    public void run() {
    	start();
    }
    
    private ServerSocket socketServer;
    
    private boolean needStop;
    public void start(){
    	if(FGBiz.dspCenter.dspSet.disableNIO){
    		acceptByMultithreading();//老代码
    	}else{//NewIO，新代码
    		acceptByNewIO();
    	}
    }
    
    private ThreadNIO threadNIO;
    private static BankResetEvent autoResetEvent;
    
    public boolean addRequest(RequestNIO reqNIO) throws Exception{
    	if(threadNIO!=null){
			threadNIO.addReq(reqNIO);
//    		autoResetEvent.set();
    		return true;
    	}else{
    		FGBiz.limitMsg.error("ServerSocketByThread.addRequest", "请求对象reqNIO为null");
    		return false;
    	}
    }
    
    private Thread nioReqThread = null;
    
    private Selector selector = null;
    private ServerSocketChannel serverSocketChannel = null;
    
    /**采用NIO*/
    public void acceptByNewIO(){
    	
    	try {
    		needStop = false;
    		autoResetEvent = new BankResetEvent();
    		//resetEvent.notifyAll();
    		threadNIO = ThreadNIO.getInstance();
    		threadNIO.setAutoResetEvent(autoResetEvent);

    		nioReqThread = new Thread(threadNIO);
    		nioReqThread.start();
    		int tmpSeqID=0;
			selector = Selector.open();
			serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);//非阻塞
            serverSocketChannel.socket().setReuseAddress(true);
            serverSocketChannel.socket().bind(new InetSocketAddress(listenPort), 5000);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while(true){
            	if(needStop){
					break;
				}
            	try{
            		if(selector.select() > 0){
                		Iterator<SelectionKey> itk = selector.selectedKeys().iterator();
                		while (itk.hasNext()) {
                			SocketChannel skChannel = null;
                			SelectionKey readyKey = null;
                			try{
                				readyKey = itk.next();
                                
                                itk.remove();//直接移除
                                if(readyKey.isAcceptable()){
                                    ServerSocketChannel serverChannel = (ServerSocketChannel)readyKey.channel();
                                    skChannel = serverChannel.accept();
                                    // region连接时，限制ip白名单
									if (!FGBiz.startLog.getLog().contains("是否开启IP白名单校验")) {
										String tipsMsg = "已开启!";
										FGBiz.startLog.addLog("=======>>>  当前DSP["+FGBiz.dspCenter.dspSet.dspCode+"]是否开启IP白名单校验: " + tipsMsg);
									}
									//获取客户端IP地址
									String remoteAddress = skChannel.socket().getInetAddress().getHostAddress();
									boolean trustedSourceIP = false;
									for (Map.Entry<String, List<String>> entry : DSPSecurityData.getInstance().getIpWhiteList().entrySet()) {
										trustedSourceIP = entry.getValue().contains(remoteAddress);
										if (trustedSourceIP) {
											break;
										}
									}
									if (!trustedSourceIP) {
										//获取服务端IP
										String localAddress = skChannel.socket().getLocalAddress().getHostAddress();
										//当服务端自身调用时，再检测一次127.0.0.1
										if (localAddress.equals(remoteAddress)) {
											trustedSourceIP = false;
											for (Map.Entry<String, List<String>> entry : DSPSecurityData.getInstance().getIpWhiteList().entrySet()) {
												trustedSourceIP = entry.getValue().contains("127.0.0.1");
												if (trustedSourceIP) {
													break;
												}
											}
											if (!trustedSourceIP) {
												FGBiz.limitMsg.warning("acceptByNewIO", "IP地址：" + remoteAddress + " IS REFUSED");
												FGBiz.startLog.addLogNoTime("来源IP[" + remoteAddress + "]访问当前DSP被拒绝，请检查config/DSPSystem.xml配置文件中'trustedSourceIP'配置节点是否正确!");
												skChannel.close();
												break;
											}
										} else {
											FGBiz.limitMsg.warning("acceptByNewIO", "IP地址：" + remoteAddress + " IS REFUSED");
											FGBiz.startLog.addLogNoTime("来源IP[" + remoteAddress + "]访问当前DSP被拒绝，请检查config/DSPSystem.xml配置文件中'trustedSourceIP'配置节点是否正确!");
											skChannel.close();
											break;
										}
									}
									//endregion
                                    skChannel.configureBlocking(false);
                                    skChannel.socket().setSoTimeout(10000);
                                    
                                    RequestNIO reqNIO = new RequestNIO(skChannel, FGBiz.dspCenter.dspSet.encodingWithDspClient);
                                	reqNIO.connTime = new Date();
                    				tmpSeqID++;
                    				reqNIO.seqIDInner = tmpSeqID;
                    				if(tmpSeqID == Integer.MAX_VALUE){
                    					tmpSeqID = 0;
                    				}
                    				skChannel.register(selector, SelectionKey.OP_READ, reqNIO);
                    				//System.out.println("skChannel.register OP_READ");
                                    
                                }else if(readyKey.isReadable()){
                                	skChannel = (SocketChannel)readyKey.channel();
                                	RequestNIO reqNIO =(RequestNIO)readyKey.attachment();
                                	
                                	reqNIO.reqSocketReceiver.clientSocketChannel = skChannel;
                                	//System.out.println("isReadable");
                    				reqNIO.receiveBytes();
                    				if(reqNIO.isStopRead()){
                    					readyKey.cancel();
                    					if(reqNIO.reqSocketReceiver.readedLength == 0){//空
                    						InetAddress srcAddr = skChannel.socket().getInetAddress();
                                        	FGBiz.limitMsg.add("ServerSocketByThread.acceptByNewIO", "关闭空连接，来源："+srcAddr.getHostAddress());
                    						skChannel.close();
                    						skChannel = null;
                    					}else{
                    						reqNIO.recCompleteTime = new Date();
                    						if(reqNIO.reqSocketReceiver.isTenantRemoting){//远程租户端连接
                    							InetAddress srcAddr = skChannel.socket().getInetAddress();
                    							FGBiz.limitMsg.addConsole("ServerSocketByThread.acceptByNewIO", "远程租户端连接，来源："+srcAddr.getHostAddress());
                    							FGBiz.tenantServer.addConnection(reqNIO);
                    						}else{
												threadNIO.addReq(reqNIO);
//                    							autoResetEvent.set();
                    						}
                    					}
                    				}
                                }else{
                                	//SelectableChannel channel = readyKey.channel();
                                	skChannel = (SocketChannel)readyKey.channel();
                                	InetAddress addr = skChannel.socket().getInetAddress();
                                	FGBiz.limitMsg.error("acceptByNewIO", "关闭不可读连接，来源："+addr.getHostAddress());
                                	skChannel.close();
                                	skChannel = null;
                                	readyKey.cancel();
                                	readyKey = null;
                                }
                			}catch(Throwable e){
                				FGBiz.limitMsg.exception("acceptByNewIO", e);
                				if(skChannel != null && (skChannel.isConnected() || skChannel.isOpen())){
                					skChannel.close();
                				}
                				if(readyKey != null){
                					readyKey.cancel();
                				}
                			}
                        }
                	}
            	}catch (Throwable e) {
            		FGBiz.limitMsg.exception("acceptByNewIO", e);
        		}
            }
		} catch (Throwable e) {
			FGBiz.limitMsg.exception("acceptByNewIO", e);
		}finally{
			try {
				if(selector!=null){
					selector.close();
				}
            } catch(Exception ex) {
            	FGBiz.limitMsg.exception("acceptByNewIO", ex);
            }  
            try {
            	if(serverSocketChannel!=null){
            		serverSocketChannel.close();
            	} 
            } catch(Exception ex) {
            	FGBiz.limitMsg.exception("acceptByNewIO", ex);
            }
		}
    }
    
    /**多线程，老模式*/
    public void acceptByMultithreading(){
    	try {
    		socketServer = new ServerSocket(listenPort, 5000);
    		needStop = false;
    		int tmpSeqID=0;
			while (true) {
				if(needStop){
					break;
				}
				Socket tmpSocket = socketServer.accept();
				tmpSocket.setSoTimeout(10000);
				//System.out.println("接收到"+tmpSeqID);
				RequestNIO tmpBizO = new RequestNIO(tmpSocket, FGBiz.dspCenter.dspSet.encodingWithDspClient);
				tmpBizO.connTime = new Date();
				tmpSeqID++;
				tmpBizO.seqIDInner = tmpSeqID;
				if(tmpSeqID == Integer.MAX_VALUE){
					tmpSeqID = 0;
				}
				tmpBizO.reqSocketReceiver = new SocketReceiver(tmpSocket, FGBiz.dspCenter.dspSet.encodingWithDspClient);
				//BizCenter.bizObjCol.add(tmpBizO);
				//开一个线程去处理
				ThreadSocket tmpEachThd = new ThreadSocket(tmpBizO);
				new Thread(tmpEachThd).start();
			}
		}
    	catch (IOException e) {
    		System.out.println("启动侦听端口："+listenPort+"异常");
    		FGBiz.limitMsg.exception("acceptByMultithreading", e);
		}
    }
    
    
    public String stop(){
    	
    	String stop1 = "关闭侦听端口"+listenPort;
    	try {
        	if(serverSocketChannel!= null){
        		serverSocketChannel.close();
        	}
        } catch(Exception ex) {
        	FGBiz.limitMsg.exception("ServerSocketByThread.stop", ex);
        }
		
		try {
			if(selector!=null){
				selector.close();
			}
        } catch(Exception ex) {
        	FGBiz.limitMsg.exception("ServerSocketByThread.stop", ex);
        }
		
		if(socketServer != null){
    		try {
    			socketServer.close();
    			socketServer = null;
			} catch (IOException e) {
				FGBiz.limitMsg.exception("ServerSocketByThread.stop", e);
			}
    	}
		
		needStop = true;
    	String stop2 = threadNIO.stop();
    	
		return StringHelper.joinNotEmpty("\r\n", stop1,stop2);
    }
}
